package com.mhys.ynnz_ssm.business.sys.aop.log;

import com.mhys.ynnz_ssm.business.sys.annotations.log.MySysLog;
import com.mhys.ynnz_ssm.business.sys.dto.LogDO;
import com.mhys.ynnz_ssm.business.sys.service.SysLogService;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Date;

/**
 * 基于aop实现的操作日志功能
 * 2024/6/14
 */
@Aspect
@Component
public class MySysLogAOP {

    // 1. 注入操作日志类的service实现层对象
    @Resource(name = "sysLogServiceImpl")
    private SysLogService sysLogService;

    // 记录执行过程花费时间的开始时间毫秒数
    private long startTimemills;

    // 描述一个切点 - 是使用了自定义日志记录注解的位置都要进行aop切入
    @Pointcut("@annotation(com.mhys.ynnz_ssm.business.sys.annotations.log.MySysLog)")
    public void sysLogPointCut(){}

    // 描述一个切点 - 用于记录异常的日志切点
    @Pointcut("@annotation(com.mhys.ynnz_ssm.business.sys.annotations.log.MySysLog)")
    public void sysLogExceptionPointCut(){}

    // 通知-前置通知-开启方法执行时间计时
    @Before("sysLogPointCut()")
    public void beforeMethod(JoinPoint point){
        startTimemills = System.currentTimeMillis();
    }

    /**
     *  1. 正常日志记录：当调用正常返回
     * @param joinPoint 切入点对象
     * @param result 返回结果
     */
    @AfterReturning(value = "sysLogPointCut()", returning = "result")
    public void afterReturn(JoinPoint joinPoint, Object result){

        // 获取RequestAttributes
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        // 从获取RequestAttributes中获取HttpServletRequest的信息
        HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);

        try {
            // 从切面织入点处通过反射机制获取织入点处的方法
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            // 获取切入点所在的方法
            Method method = signature.getMethod();

            // 获取头部的注解
            MySysLog annotation = method.getAnnotation(MySysLog.class);

            // 开始设置要保存的日志对象
            LogDO sysLogDO = new LogDO();

            //万一没有写日志注解也不能出错
            if(annotation!=null){
                sysLogDO.setModelName(annotation.modelName());
                sysLogDO.setMethodType(annotation.methodType());
                sysLogDO.setMethodDesc(annotation.methodDesc());
            }

            // 将入参转换成json
            String params = Arrays.toString(joinPoint.getArgs());
            // 获取请求的类名
            String className = joinPoint.getTarget().getClass().getName();

            // 获取请求的方法名
            String methodName = method.getName();
            methodName = className + "." + methodName + "()";

            // 将获得的数据赋值到logDO对象中
            sysLogDO.setPath(request.getRequestURI());
            sysLogDO.setGetTime(new Date(this.startTimemills));
            sysLogDO.setOverTime(new Date());
            long l = System.currentTimeMillis() - this.startTimemills;
            sysLogDO.setTime( String.valueOf(l));
            sysLogDO.setMethodName(methodName);
            sysLogDO.setParameter(params);
            sysLogDO.setResType(1);

            // 保存到数据库
            sysLogService.addLog(sysLogDO);

        }catch (Exception e){
            e.printStackTrace();
        }

    }

    /**
     *  当出现异常时，保存异常日志信息
     * @param joinPoint
     * @param e
     */
    @AfterThrowing(value = "sysLogExceptionPointCut()", throwing = "e")
    public void afterException(JoinPoint joinPoint, Throwable e){
        // 获取RequestAttributes
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        // 从获取RequestAttributes中获取HttpServletRequest的信息
        HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);

        try {
            // 从切面织入点处通过反射机制获取织入点处的方法
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            // 获取切入点所在的方法
            Method method = signature.getMethod();

            // 获取头部的注解
            MySysLog annotation = method.getAnnotation(MySysLog.class);

            // 开始设置要保存的日志对象
            LogDO sysLogDO = new LogDO();

            //万一没有写日志注解也不能出错
            if(annotation!=null){
                sysLogDO.setModelName(annotation.modelName());
                sysLogDO.setMethodType(annotation.methodType());
                sysLogDO.setMethodDesc(annotation.methodDesc());
            }

            // 将入参转换成json
            String params = Arrays.toString(joinPoint.getArgs());
            // 获取请求的类名
            String className = joinPoint.getTarget().getClass().getName();

            // 获取请求的方法名
            String methodName = method.getName();
            methodName = className + "." + methodName + "()";

            // 将获得的数据赋值到logDO对象中
            sysLogDO.setPath(request.getRequestURI());
            sysLogDO.setGetTime(new Date(this.startTimemills));
            sysLogDO.setOverTime(new Date());
            long l = System.currentTimeMillis() - this.startTimemills;
            sysLogDO.setTime( String.valueOf(l));
            sysLogDO.setMethodName(methodName);
            sysLogDO.setParameter(params);
            sysLogDO.setResType(2);

            // 保存到数据库
            sysLogService.addLog(sysLogDO);

        }catch (Exception e2){
            e2.printStackTrace();
        }
    }

}
